﻿using System;
using System.Collections.ObjectModel;
using System.Data.Services.Client;
using System.Diagnostics;
using System.Linq;
using nRoute.Components;
using nRoute.ViewModels;

namespace nRoute.Samples.Netflix.ViewModels
{
    public abstract class DataListingViewModelBase<T, C>
        : NavigationViewModelBase, ISupportSearching
        where
            C : DataServiceContext
    {
        private const string DATA_KEY = "DataKey";
        private const string SEARCH_KEY = "SearchKey";
        private const string SEARCH_INDEX_KEY = "SearchIndexKey";
        private const string ISLOADED_KEY = "IsLoadedKey";
        private const int PAGE_SIZE = 30;

        private C _context;
        private DataServiceCollection<T> _list;
        private int _pageIndex;

        protected DataListingViewModelBase(C context)
        {
            _context = context;
            SetupCommands();
        }

#region Properties

        public ObservableCollection<T> Listing 
        {
            get 
            {
                if (_list == null && !IsLoading) LoadData();
                return _list; 
            }
        }

        public bool IsLoading { get; private set; }

        public bool IsLoaded { get; private set; }

        public string SearchText { get; private set; }

        public ActionCommand LoadMoreCommand { get; private set; }

        protected C Context
        {
            get { return _context; }
        }

#endregion

#region ISearachable Implementation

        public void Search(string searchText)
        {
            if (string.IsNullOrWhiteSpace(searchText)) throw new ArgumentNullException("searchText");

            _pageIndex = 0;
            SearchText = searchText;
            NotifyPropertyChanged(() => SearchText);
            NotifyPropertyChanged(() => IsSearching);

            _list.Clear();
            LoadData();
        }

        public bool IsSearching
        {
            get { return (!string.IsNullOrWhiteSpace(this.SearchText)); }
        }

        public void CancelSearch()
        {
            _pageIndex = 0;
            SearchText = null;
            NotifyPropertyChanged(() => SearchText);
            NotifyPropertyChanged(() => IsSearching);

            _list.Clear();
            LoadData();
        }

#endregion

#region Overrides

        protected override void OnRestoreState(ParametersCollection state)
        {
            if (state != null && state.ContainsKey(DATA_KEY))
            {
                _list = (DataServiceCollection<T>)state[DATA_KEY];
                _list.LoadCompleted += Data_LoadCompleted;

                _pageIndex = Convert.ToInt32(state[SEARCH_INDEX_KEY]);
                this.SearchText = Convert.ToString(state[SEARCH_KEY]);
                NotifyPropertyChanged(() => IsSearching);

                this.IsLoaded = Convert.ToBoolean(state[ISLOADED_KEY]);
                NotifyPropertyChanged(() => IsLoaded);

                if (_list.Count == 0) LoadData();
            }
        }

        protected override ParametersCollection OnSaveState()
        {
            if (_list != null)
            {
                _list.LoadCompleted -= Data_LoadCompleted;
                return new ParametersCollection()  
                {
                    new Parameter(DATA_KEY, _list),
                    new Parameter(SEARCH_KEY, this.SearchText),
                    new Parameter(SEARCH_INDEX_KEY, _pageIndex),
                    new Parameter(ISLOADED_KEY, this.IsLoaded)
                };
            }
            else
            {
                return null;
            }
        }

#endregion

#region Must Override

        protected abstract IQueryable<T> CreateQuery(string searchText);

#endregion
        
#region Helpers

        private void SetupCommands()
        {
            this.LoadMoreCommand = new ActionCommand(() =>
            {
                _pageIndex += 1;
                LoadData();
            });
        }

        private void LoadData()
        {
            if (_list == null)
            {
                _list = new DataServiceCollection<T>();
                _list.LoadCompleted += Data_LoadCompleted;
            }

            IsLoading = true;
            NotifyPropertyChanged(() => IsLoading);

            IQueryable<T> _query = this.CreateQuery(this.SearchText);
            _query = _query.Skip(_pageIndex * PAGE_SIZE).Take(PAGE_SIZE);

            try
            {
                _list.LoadAsync(_query);
            }
            catch
            {
                // the problem is to Cancel any existing request we need to switch to the BegingXX API
                Debugger.Break();

                // reset
                _pageIndex = 0;
                _list.LoadCompleted -= Data_LoadCompleted;
                _list = null;                
                LoadData();
                NotifyPropertyChanged(() => Listing);
            }
        }

        private void Data_LoadCompleted(object sender, LoadCompletedEventArgs e)
        {
            IsLoading = false;
            NotifyPropertyChanged(() => IsLoading);

            IsLoaded = e.QueryOperationResponse.TotalCount <= _list.Count;
            NotifyPropertyChanged(() => IsLoaded);            
        }

#endregion

    }
}